【Swift】アプリのバージョンの取得と比較を試してみた
アプリのバージョンの取得と比較を試してみたので記事にしておこうと思います。
環境
- Xcode 13.3
アプリバージョン取得
アプリバージョン取得は、Bundle.main.object
からCFBundleShortVersionString
キーを使用することで取得出来ます。
let currentAppVersionString = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String
このバージョンは、TARGETS > General > Identity > Version の値を取得します。
アプリバージョンの比較
文字列で比較する
文字列でもcompare
メソッドや比較演算子を使用して比較することが出来ます。
ただ、10.0.0
のようなメジャーバージョンが二桁のケースと3.2.1
を比較した場合に想定していた結果になりませんでした。
let requiredVersion = "10.0.0" let currentVersion = "3.2.1" if requiredVersion.compare(currentVersion) == .orderedDescending { print("requiredVersionの方が大きい") } else { print("currentVersionの方が大きい") } // 出力 currentVersionの方が大きい if requiredVersion > currentVersion { print("requiredVersionの方が大きい") } else { print("currentVersionの方が大きい") } // 出力 currentVersionの方が大きい
いずれも3.2.1
の方が10.0.0
より大きいという結果になってしまいました。
AppVersionで比較する
文字列比較では想定した結果にならなかった為、AppVersion
という構造体を作成してアプリバージョンを比較出来るようにします。
前提条件
- 渡されるバージョンはセマンティックバージョニングのものであること
- 上記条件を満たさない場合は
nil
を返す
今回はこの条件にして1.0
のようなセマンティックバージョニングでは無いものを許容しないようにしました。ですが、その時々の仕様に合わせて変更していただけたらと思います。
struct AppVersion
struct AppVersion { let versionString: String let majorVersion: Int let minorVersion: Int let patchVersion: Int init?(_ version: String) { let versionNumbers = version.components(separatedBy: ".").compactMap { Int($0) } if versionNumbers.count == 3 { versionString = version majorVersion = versionNumbers[0] minorVersion = versionNumbers[1] patchVersion = versionNumbers[2] } else { print("Does not meet the conditions of Semantic Versioning. App Version: \(version)") return nil } } }
プロパティ
- versionString: String
- アプリバージョン文字列
- majorVersion: Int
- メジャーバージョン
- minorVersion: Int
- マイナーバージョン
- patchVersion: Int
- パッチバージョン
init
init
時にString
型のアプリバージョンを受け取り、そのバージョンを.
区切りをして、その区切った値をInt
型に変更しています。
セマンティックバージョニング
で、メジャー、マイナー、パッチバージョンがある想定なので、.
区切りで配列にした値の要素数が3
では無い場合、今回はnil
を返しています。
要素数が3
つある場合は、それぞれの要素をそれぞれのバージョン番号の定数に渡しています。
比較する処理を実装
AppVersion
型で比較演算子を用いて比較出来るようにする為、Compareble
に準拠させます。
Compareble
はEquatable
に準拠している為、==
のメソッドも作成する必要があります。
// MARK: - AppVersion Comparable Functions extension AppVersion: Comparable { // 左辺と右辺は等しい static func == (lhs: AppVersion, rhs: AppVersion) -> Bool { if lhs.majorVersion == rhs.majorVersion, lhs.minorVersion == rhs.minorVersion, lhs.patchVersion == rhs.patchVersion { return true } return false } // 左辺は右辺より小さい static func < (lhs: AppVersion, rhs: AppVersion) -> Bool { if lhs.majorVersion != rhs.majorVersion { return lhs.majorVersion < rhs.majorVersion } if lhs.minorVersion != rhs.minorVersion { return lhs.minorVersion < rhs.minorVersion } if lhs.patchVersion != rhs.patchVersion { return lhs.patchVersion < rhs.patchVersion } return false } // 左辺は右辺より大きい static func > (lhs: AppVersion, rhs: AppVersion) -> Bool { if lhs.majorVersion != rhs.majorVersion { return lhs.majorVersion > rhs.majorVersion } if lhs.minorVersion != rhs.minorVersion { return lhs.minorVersion > rhs.minorVersion } if lhs.patchVersion != rhs.patchVersion { return lhs.patchVersion > rhs.patchVersion } return false } // 左辺は右辺以下 static func <= (lhs: AppVersion, rhs: AppVersion) -> Bool { if lhs == rhs { return true } return lhs < rhs } // 左辺は右辺以上 static func >= (lhs: AppVersion, rhs: AppVersion) -> Bool { if lhs == rhs { return true } return lhs > rhs } }
左辺と右辺は等しい
メジャーバージョンからパッチバージョンまでの全てのバージョン番号が一致する場合は、true
を返しています。そうではない場合はfalse
を返しています。
static func == (lhs: AppVersion, rhs: AppVersion) -> Bool { if lhs.majorVersion == rhs.majorVersion, lhs.minorVersion == rhs.minorVersion, lhs.patchVersion == rhs.patchVersion { return true } return false }
左辺は右辺より大きい
まずはメジャーバージョン同士で比較を行い、等しい場合は、マイナーバージョンとパッチバージョンの比較に移っていきます。バージョンの比較で等しく無い場合の時だけそのバージョン同士で左辺が右辺より大きいかどうかの判定をしています。
左辺は右辺より小さいかどうかの判定の場合も処理内容はほぼ同じで、バージョンの比較でイコールでは無い場合の時だけそのバージョン同士で左辺は右辺より小さいかどうかの判定をしています。
static func > (lhs: AppVersion, rhs: AppVersion) -> Bool { if lhs.majorVersion != rhs.majorVersion { return lhs.majorVersion > rhs.majorVersion } if lhs.minorVersion != rhs.minorVersion { return lhs.minorVersion > rhs.minorVersion } if lhs.patchVersion != rhs.patchVersion { return lhs.patchVersion > rhs.patchVersion } return false }
左辺は右辺以下、左辺は右辺以上
等しいかどうかの判定と、より大きい、またはより小さいの判定を組み合わせて比較結果を出します。
static func <= (lhs: AppVersion, rhs: AppVersion) -> Bool { if lhs == rhs { return true } return lhs < rhs } static func >= (lhs: AppVersion, rhs: AppVersion) -> Bool { if lhs == rhs { return true } return lhs > rhs }
比較結果
10.0.0
と3.2.1
を比較した時に無事にrequiredVersion(10.0.0)の方が大きいという結果を得ることが出来ました。
guard let requiredVersion = AppVersion("10.0.0"), let currentVersion = AppVersion("3.2.1") else { return } if requiredVersion > currentVersion { print("requiredVersionの方が大きい") } else { print("currentVersionの方が大きい") } // 出力 requiredVersionの方が大きい
おわりに
文字列でも比較は出来るが、想定外の結果になるパターンがあることが分かりました。比較をするクラスや構造体を作成するには、Comparable
に準拠させましょう!
lhs
とrhs
ってなんだよ!と思ったら、left hand side(左辺)とright hand side(右辺)の略語でした。
この記事が誰かの助けになればと思います。